import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_loggy_dio/flutter_loggy_dio.dart';
import 'package:get_storage/get_storage.dart';
import 'package:tushare_flutter/http/global.dart';
import 'package:tushare_flutter/utils/log_utils.dart';

import 'http_config.dart';
import 'http_parse.dart';
import 'http_response.dart';
import 'http_transformer.dart';

class HttpClient {
  late final Dio _dio;

  HttpClient({BaseOptions? options, HttpConfig? dioConfig}){
    options ??= BaseOptions(
      baseUrl: dioConfig?.baseUrl ?? "",
      contentType: 'application/json',
      connectTimeout: Duration(seconds: dioConfig?.connectTimeout ?? Duration.millisecondsPerMinute),
      sendTimeout: Duration(seconds: dioConfig?.sendTimeout ?? Duration.millisecondsPerMinute),
      receiveTimeout: Duration(seconds: dioConfig?.receiveTimeout ?? Duration.millisecondsPerMinute),
    )..headers = dioConfig?.headers;

    _dio = Dio(options);
    final cacheOptions = CacheOptions(
      // A default store is required for interceptor.
      store: MemCacheStore(),
      // Optional. Returns a cached response on error but for statuses 401 & 403.
      hitCacheOnErrorExcept: [401, 403],
      // Optional. Overrides any HTTP directive to delete entry past this duration.
      maxStale: const Duration(days: 7),
    );
    _dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
    // Cookie管理
    if (dioConfig?.cookiesPath?.isNotEmpty ?? false) {
      _dio.interceptors.add(CookieManager(PersistCookieJar(storage: FileStorage(dioConfig!.cookiesPath))));
    }

    if (kDebugMode) {
      _dio.interceptors.add(LoggyDioInterceptor(requestHeader: true, requestBody: true, error: true,));
    }
    if (kDebugMode) {
      _dio.interceptors.add(LogInterceptor(
        responseBody: true,
        error: true,
        requestHeader: false,
        responseHeader: false,
        request: false,
        requestBody: true,
      ));
    }
    if (dioConfig?.interceptors?.isNotEmpty ?? false) {
      _dio.interceptors.addAll(dioConfig!.interceptors!);
    }
  }

  /// 设置鉴权请求头
  Map<String, dynamic> _getAuthorizationHeader() {
    Map<String, dynamic> headers;
    String token = Global.accessToken;
    if (token.isEmpty) {
      LogUtils.d("token is empty");
      var storageBox = GetStorage();
      var saveToken = storageBox.read("token");
      if (saveToken != null) {
        token = storageBox.read("token");
        Global.accessToken = token;
      }
    }
    if (token.isNotEmpty) {
      headers = {
        'Authorization': "Bearer $token",
        "X-Client-Ver": "66",
        "X-Client-Platform": "Android",
        "X-Push-Id": "65l08n5nwtip5vk",
        "X-Device-Location": "28.252427,113.11691",
      };
    } else {
      LogUtils.w("token is null");
      headers = {
        "X-Client-Ver": "66",
        "X-Client-Platform": "Android",
        "X-Push-Id": "65l08n5nwtip5vk",
        "X-Device-Location": "28.252427,113.11691",
      };
    }
    return headers;
  }

  Future<HttpResponse> get(String uri,
      {Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onReceiveProgress,
      HttpTransformer? httpTransformer}) async {
    try {
      Options requestOptions = options ?? Options();
      Map<String, dynamic>? authorization = _getAuthorizationHeader();
      requestOptions = requestOptions.copyWith(headers: authorization);
      var response = await _dio.get(
        uri,
        queryParameters: queryParameters,
        options: requestOptions,
        cancelToken: cancelToken,
        onReceiveProgress: onReceiveProgress,
      );
      return handleResponse(response, httpTransformer: httpTransformer);
    } on Exception catch (e) {
      return handleException(e);
    }
  }

  Future<HttpResponse> post(String uri,
      {data,
      Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress,
      HttpTransformer? httpTransformer}) async {
    try {
      Options requestOptions = options ?? Options();
      Map<String, dynamic>? authorization = _getAuthorizationHeader();
      requestOptions = requestOptions.copyWith(headers: authorization);
      var response = await _dio.post(
        uri,
        data: data,
        queryParameters: queryParameters,
        options: requestOptions,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      return handleResponse(response, httpTransformer: httpTransformer);
    } on Exception catch (e) {
      return handleException(e);
    }
  }

  Future<HttpResponse> patch(String uri,
      {data,
      Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      ProgressCallback? onReceiveProgress,
      HttpTransformer? httpTransformer}) async {
    try {
      Options requestOptions = options ?? Options();
      Map<String, dynamic>? authorization = _getAuthorizationHeader();
      requestOptions = requestOptions.copyWith(headers: authorization);
      var response = await _dio.patch(
        uri,
        data: data,
        queryParameters: queryParameters,
        options: requestOptions,
        cancelToken: cancelToken,
        onSendProgress: onSendProgress,
        onReceiveProgress: onReceiveProgress,
      );
      return handleResponse(response, httpTransformer: httpTransformer);
    } on Exception catch (e) {
      return handleException(e);
    }
  }

  Future<HttpResponse> delete(String uri,
      {data,
      Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      HttpTransformer? httpTransformer}) async {
    try {
      Options requestOptions = options ?? Options();
      Map<String, dynamic>? authorization = _getAuthorizationHeader();
      requestOptions = requestOptions.copyWith(headers: authorization);
      var response = await _dio.delete(
        uri,
        data: data,
        queryParameters: queryParameters,
        options: requestOptions,
        cancelToken: cancelToken,
      );
      return handleResponse(response, httpTransformer: httpTransformer);
    } on Exception catch (e) {
      return handleException(e);
    }
  }

  Future<HttpResponse> put(String uri,
      {data,
      Map<String, dynamic>? queryParameters,
      Options? options,
      CancelToken? cancelToken,
      HttpTransformer? httpTransformer}) async {
    try {
      Options requestOptions = options ?? Options();
      Map<String, dynamic>? authorization = _getAuthorizationHeader();
      requestOptions = requestOptions.copyWith(headers: authorization);
      var response = await _dio.put(
        uri,
        data: data,
        queryParameters: queryParameters,
        options: requestOptions,
        cancelToken: cancelToken,
      );
      return handleResponse(response, httpTransformer: httpTransformer);
    } on Exception catch (e) {
      return handleException(e);
    }
  }

  Future<Response> download(String urlPath, savePath,
      {ProgressCallback? onReceiveProgress,
      Map<String, dynamic>? queryParameters,
      CancelToken? cancelToken,
      bool deleteOnError = true,
      String lengthHeader = Headers.contentLengthHeader,
      data,
      Options? options,
      HttpTransformer? httpTransformer}) async {
    try {
      var response = await _dio.download(
        urlPath,
        savePath,
        onReceiveProgress: onReceiveProgress,
        queryParameters: queryParameters,
        cancelToken: cancelToken,
        deleteOnError: deleteOnError,
        lengthHeader: lengthHeader,
        data: data,
        options: data,
      );
      return response;
    } catch (e) {
      rethrow;
    }
  }
}
